package org.acghub.mtdb.core.log;

import org.acghub.mtdb.core.LogKey;
import org.acghub.mtdb.core.LogNotify;
import org.acghub.mtdb.core.Logs;

import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;

public abstract class LogAbstractSet<E> extends LogAbstractCollection<E> implements Set<E> {

    public LogAbstractSet(LogKey logkey, Set<E> wrapped) {
        super(logkey, wrapped);
    }

    protected LogAbstractSet(LogAbstractCollection<E> root, Set<E> wrapped) {
        super(root, wrapped);
    }

    @Override
    protected Set<E> wrapped() {
        return (Set<E>) wrapped;
    }

    @Override
    public void forEach(Consumer<? super E> action) {
        super.forEach(action);
    }

    @Override
    protected LogAbstractCollection<E>.CollectionLog createLog() {
        return new SetLog();
    }

    protected class SetLog extends CollectionLog {

        protected final Set<E> added = new HashSet<>();
        protected final Set<E> removed = new HashSet<>();
        protected final Set<E> eldest = new HashSet<>();

        private void add0(E e) {
            if (!removed.remove(e)) {
                added.add(e);
            }
        }

        private void remove0(E e) {
            if (added.remove(e)) {
                return;
            }
            removed.add(e);
            eldest.add(e);
        }


        @Override
        public boolean isChanged() {
            return !added.isEmpty() || !removed.isEmpty();
        }

        @Override
        public void clear() {
            added.clear();
            removed.clear();
            eldest.clear();
        }

        @Override
        public void commit() {
            if (isChanged()) {
                LogNotify.notify(logkey, this);
            }
        }

        @Override
        public void rollback() {
            wrapped.removeAll(added);
            wrapped.addAll(removed);
            wrapped.removeAll(eldest);
            wrapped.addAll(eldest);
            clear();
        }

        @Override
        protected void onRemove(E e) {
            remove0(e);
            Logs.link(e, null, null);
        }

        @Override
        protected void onAdd(E e) {
            add0(e);
            Logs.link(e, logkey.getBean(), logkey.getVarname());
        }

        @Override
        public String toString() {
            return "SetLog{" + "added=" + added + ", removed=" + removed + ", eldest=" + eldest + '}';
        }
    }
}
